package com.iring.service.sms.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.authy.AuthyApiClient;
import com.authy.api.Params;
import com.authy.api.PhoneInfo;
import com.authy.api.PhoneInfoResponse;
import com.authy.api.PhoneVerification;
import com.authy.api.Verification;
import com.iring.domain.config.prop.SMSProperties;
import com.iring.domain.sms.SMS;
import com.iring.exception.ParamInvalidException;
import com.iring.service.sms.MessageService;
import com.iring.exception.config.ErrorEnum;
import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.type.PhoneNumber;

/**
 * @author wanggan
 */
@Service
public class AuthyMessageServiceImpl implements MessageService {

    private static final Logger logger = LoggerFactory.getLogger(AuthyMessageServiceImpl.class);

    @Autowired
    AuthyApiClient client;

    @Autowired
    SMSProperties smsProperties;

    private void checkStringParam(String str, String name) {
        if (null == str  || str.trim()
                .equals("")) {
            throw new ParamInvalidException(name + "is null");
        }
    }

    private void checkCodeParam(String code) {
        if (null == code || code.trim().equals("")) {
            throw new ParamInvalidException("code is null");
        } else if (code.length() <= 3) {
            throw new ParamInvalidException("invalid code length");
        }
    }

    @Override
    public Boolean sendMsg(String sendToNumber) {
        Twilio.init(smsProperties.getAccountSid(), smsProperties.getAuthToken());

        Message.creator(new PhoneNumber(smsProperties.getSendFrom()), new PhoneNumber(sendToNumber),
                smsProperties.getTipMsg())
                .create();
        return Boolean.TRUE;
    }

    @Override
    public Boolean sendCode(SMS sms) {
        String countryCode = sms.getCountryCode();
        String contactPhonenum = sms.getContactPhonenum();

        Boolean noCountryCode = StringUtils.isEmpty(countryCode);
        Boolean noContactPhonenum = StringUtils.isEmpty(contactPhonenum);
        if(noCountryCode || noContactPhonenum){
            return Boolean.FALSE;
        }
        sms = setMsgParam(sms);
        if (sendMsgCode(sms)) {
            return Boolean.TRUE;
        } else {
            if (sendMsgCode(sms)) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }
    }

    /**
     * 检验验证码
     *
     * @param sms
     *            {@link SMS}
     * @return 检查结果
     */
    @Override
    public Boolean checkCode(SMS sms) {
        String phoneNum = sms.getContactPhonenum();
        String countryCode = sms.getCountryCode();
        String msgcode = sms.getMsgCode();

        Boolean noContactPhonenum = StringUtils.isEmpty(phoneNum);
        Boolean noCountryCode = StringUtils.isEmpty(countryCode);
        Boolean noMsgcode = StringUtils.isEmpty(msgcode);
        if(noCountryCode || noContactPhonenum || noMsgcode){
            return Boolean.FALSE;
        }
        if(msgcode.length() <= 3){
            return Boolean.FALSE;
        }
        String msg = checkCode(phoneNum, countryCode, msgcode);
        if ("success".equals(msg)) {
            return Boolean.TRUE;
        } else if ("fail".equals(msg)) {
            throw new ParamInvalidException(ErrorEnum.INVALID_SMS_CODE);
        } else if ("uncheck".equals(msg)) {
            throw new ParamInvalidException(ErrorEnum.USER_UNCHECK_CODE_ERROR);
        } else {
            msg = checkCode(phoneNum, countryCode, msgcode);
            if ("success".equals(msg)) {
                return Boolean.TRUE;
            } else if ("fail".equals(msg)) {
                throw new ParamInvalidException(ErrorEnum.INVALID_SMS_CODE);
            } else if ("uncheck".equals(msg)) {
                throw new ParamInvalidException(ErrorEnum.USER_UNCHECK_CODE_ERROR);
            } else {
                throw new ParamInvalidException(ErrorEnum.NETWORK_EXCEPTION);
            }
        }
    }

    /**
     * 校验验证码
     *
     * @param phone
     *            电话号码
     * @param countryCode
     *            国家代码
     * @param msgcode
     *            验证码
     *            error 网络异常 success 正确 fail 验证码错误
     * @return 验证结果
     */
    private String checkCode(String phone, String countryCode, String msgcode) {
        try {
            PhoneVerification phoneVerification = client.getPhoneVerification();
            Verification verification = phoneVerification.check(phone, countryCode, msgcode);
            logger.info("verification={}", verification.toJSON());
            if ("true".equalsIgnoreCase(verification.getSuccess())) {
                // 验证码正确
                return "success";
            } else {
                if ("Verification code is incorrect".equals(verification.getMessage())) {
                    // 验证码错误
                    return "fail";
                } else if (verification.getMessage()
                        .contains("No pending verifications for")) {
                    return "uncheck";
                }
                return "error";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
    }

    /**
     * 设置短信参数
     *
     * @param sms
     *            {@link SMS}设置短信发送的参数
     * @return {@link SMS}返回设置完成后的短信验证码参数
     */
    private SMS setMsgParam(SMS sms) {
        String countryCode = sms.getCountryCode();
        //如果是86的话,默认设置为中文的语言,设置语言类型
        if ("86".equals(countryCode)) {
            sms.setLanguage("zh-CN");
        }
        //获取话机类型
        String phonetype = getPhonetype(sms);
        // 设置发送类型
        if ("cellphone".equals(phonetype)) {
            sms.setSendType("sms");
        }
        return sms;
    }

    /**
     * 多次尝试获取话机的类型
     *
     * @param sms
     *            {@link SMS}获取话机类型的参数
     * @return 话机类型
     */
    private String getPhonetype(SMS sms) {
        String phoneNum = sms.getContactPhonenum();
        String countryCode = sms.getCountryCode();
        String type = getPhoneType(phoneNum, countryCode);
        if (!("false".equals(type))) {
            return type;
        } else {
            // 进行二次判断
            type = getPhoneType(phoneNum, countryCode);
            if (!("false".equals(type))) {
                return type;
            } else {
                throw new ParamInvalidException(ErrorEnum.NETWORK_EXCEPTION);
            }
        }
    }

    /**
     * 获取电话的类型
     *
     * @param phone
     *            电话号码
     * @param countryCode
     *            国家代码
     * @return 话机类型
     */
    private String getPhoneType(String phone, String countryCode) {
        try {
            PhoneInfo phoneInfo = client.getPhoneInfo();
            PhoneInfoResponse phoneInfoResponse;
            phoneInfoResponse = phoneInfo.info(phone, countryCode);
            if ("false".equals(phoneInfoResponse.getSuccess())) {// 服务器异常
                if ("Couldn't get phone information: Phone number is invalid"
                        .equalsIgnoreCase(phoneInfoResponse.getMessage())) {
                    return "landline";
                }
                return "false";
            } else {
                String type = phoneInfoResponse.getType();
                if ("unknown".equalsIgnoreCase(type)) {
                    type = "landline";
                }
                return type;
            }
        } catch (NullPointerException e) {
            logger.error("exception ={}",e);
            return "false";
        }
    }

    /**
     * 发送验证码
     *
     * @param sms
     *            发送短信验证码的参数
     * @return 发送是否成功
     */
    private Boolean sendMsgCode(SMS sms) {
        try {
            PhoneVerification phoneVerification = client.getPhoneVerification();
            Params params = new Params();
            params.setAttribute("locale", sms.getLanguage());
            Verification verification = phoneVerification.start(sms.getContactPhonenum(),
                    sms.getCountryCode(), sms.getSendType(), params);
            if (verification == null) {
                return Boolean.FALSE;
            }
            if ("true".equals(verification.getSuccess())) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;

        } catch (Exception e) {
            logger.error("exception ={}",e);
            return Boolean.FALSE;
        }
    }
}
